/* 
 * File:   Character.cpp
 * Author: paula
 * 
 * Created on 8 de septiembre de 2014, 23:26
 */

#include "Character.h"

Character::Character() {
}

bool Character::characterOverlap(int x, int y) {

    b2Manifold mani;
    mani.pointCount = 0;
    //pos de Mario.
    b2Rot rotA(0);
    //Coordinate element A
    b2Vec2 vecA(x, y);
    b2Transform tranA(vecA, rotA);
    //creo el shape de mario.
    b2PolygonShape boxA;
    boxA.SetAsBox(this->objWidth / 2, this->objHeight / 2);

    //GROUND
    b2Rot rotGround(0);
    b2Vec2 vecGround(this->windowUnitWidth / 2, this->windowUnitHeight);
    b2Transform tranGround(vecGround, rotGround);
    b2PolygonShape boxGround;
    boxGround.SetAsBox(this->windowUnitWidth / 2, (this->windowUnitHeight / 2)*0.05);
    //comparo
    b2CollidePolygons(&mani, &boxA, tranA, &boxGround, tranGround);
    if (mani.pointCount > 0) {
        return true;
    }
    //CEILING
    b2Rot rotCeiling(0);
    b2Vec2 vecCeiling(this->windowUnitWidth / 2, 0);
    b2Transform tranCeiling(vecCeiling, rotCeiling);
    b2PolygonShape boxCeiling;
    boxCeiling.SetAsBox(this->windowUnitWidth / 2, 2);
    b2CollidePolygons(&mani, &boxA, tranA, &boxCeiling, tranCeiling);

    if (mani.pointCount > 0) {
        return true;
    }
    //LEFTWALL
    b2Rot rotLeftWall(0);
    b2Vec2 vecLeftWall(0, this->windowUnitHeight / 2);
    b2Transform tranLeftWall(vecLeftWall, rotLeftWall);
    b2PolygonShape boxLeftWall;
    boxLeftWall.SetAsBox(2, this->windowUnitHeight / 2);
    b2CollidePolygons(&mani, &boxA, tranA, &boxLeftWall, tranLeftWall);
    if (mani.pointCount > 0) {
        return true;
    }
    //rightWall RIGHTWALL
    b2Rot rotRightWall(0);
    b2Vec2 vecRightWall(this->windowUnitWidth, this->windowUnitHeight / 2);
    b2Transform tranRightWall(vecRightWall, rotRightWall);
    b2PolygonShape boxRightWall;
    boxRightWall.SetAsBox(2, (this->windowUnitHeight / 2));
    //comparo
    b2CollidePolygons(&mani, &boxA, tranA, &boxRightWall, tranRightWall);
    if (mani.pointCount > 0) {
        return true;
    }
    return false;
}

Character::Character(ObjFactory *objFactory, int x, int y) {

    this->myLogFile->getInstance(MY_LOG_MODE);
    this->objFactory = objFactory;
    this->windowPixelWidth = this->objFactory->getPixelWidth();
    this->windowPixelHeight = this->objFactory->getPixelHeight();
    this->windowUnitWidth = this->objFactory->getUnitWidth();
    this->windowUnitHeight = this->objFactory->getUnitHeight();
    this->objWidth = (0.08 * windowUnitWidth) * PRINCIPAL_SCALE;
    this->objHeight = (0.18 * windowUnitHeight) * PRINCIPAL_SCALE;
    Dot *dotito = new Dot(x, y, objWidth, objHeight);
    string lineToLog = "";

    //si el personaje se superpone con los bordes del mapa, cambio su posicion.
    if (this->characterOverlap(x, y)) {
        dotito->setPos(this->windowUnitWidth / 2, this->windowUnitHeight / 2);
        lineToLog = "[1] El personaje choca con los bordes del  mapa  en x: " + to_string(x) + ", y:" + to_string(y) + ". Cambia su posicion al centro del mapa. \n";
        myLogFile->writeInLog(1, lineToLog);
    }
    //Si el personaje esta fuera de los limites del mapa, cambio su posicion.
    //    if ((x < 0) || (y < 0) || (x >this->windowPixelWidth) || (y> this->windowPixelHeight)) {
    if ((x < 0) || (y < 0) || (x >this->windowUnitWidth) || (y> this->windowUnitHeight)) {
        lineToLog = "[1] El personaje esta fuera de los limites del mapa x: " + to_string(x) + ", y:" + to_string(y) + ". Cambia su posicion al centro del mapa. \n";
        myLogFile->writeInLog(1, lineToLog);
        dotito->setPos(this->windowPixelWidth / 2, this->windowPixelHeight / 2);
    }
    this->dot = dotito;
    Object *object = objFactory->getCharacter(this->dot->dotPixelsToWorld(), this->dot->sizeDotPixelsToWorld());
    this->object = object;
    this->gRenderer = objFactory->getRender();
    this->directionRight = true;
    this->counterRight = 1;
    this->counterLeft = 1;
    this->counterRightII = 1;
    this->counterLeftII = 1;
    this->counterKneel = 1;
    this->counterJump = 1;
    this->counterWait = 9;
    this->beginning = true;
    this->delayWalk = 10;
    this->delay = 20;
    this->jumping = false;
    this->kneeling = false;
    this->walking = false;
    this->fallingDown = false;
    this->wasJumping = false;
    this->lastPosY = 0;
    this->currentPosY = 0;
    this->lastVelY = 0;
    this->currentVelY = 0;
    this->initConsFall = 3;
    this->constFall = initConsFall;
    
    float velocityWalkLeft = 0.0;
    float velocityWalkRight = 0.0;

    this->loadMedia();

}

void Character::actualizePosition() {
    b2Vec2 currentPos = (this->object->getPosition());
    this->dot->setPos(currentPos.x, currentPos.y);
}

void Character::changeImage(SBSImage* image, int delay) {

    SDL_Texture* mTexture = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, this->objFactory->getUnitWidth(), this->objFactory->getUnitHeight());
    SDL_SetRenderTarget(gRenderer, mTexture);
    SDL_SetTextureBlendMode(mTexture, SDL_BLENDMODE_BLEND);
    SDL_RenderClear(gRenderer);
    SDL_Rect* principalSettings = new SDL_Rect();
    b2Vec2* dotito = new b2Vec2();
    *dotito = *this->dot->dotWorldToPixels();
    principalSettings->w = ((windowUnitWidth * image->getWidth()) / SCREEN_WIDTH) * PRINCIPAL_SCALE;
    principalSettings->h = ((windowUnitHeight * image->getHeight()) / SCREEN_HEIGHT) * PRINCIPAL_SCALE;
    principalSettings->x = ((dotito->x) - ((objWidth) / 2));
    principalSettings->y = ((dotito->y) - ((objHeight) / 2));

    printf("Posicion x %d  y %d\n",principalSettings->x,principalSettings->y);

    SDL_RenderCopy(gRenderer, (image->getTexture()), NULL, principalSettings);
    SDL_SetRenderTarget(gRenderer, NULL);
    SDL_RenderCopyEx(gRenderer, mTexture, NULL, NULL, 0, NULL, SDL_FLIP_NONE);
    SDL_DestroyTexture(mTexture);
    mTexture = NULL;
    //        SDL_Delay(200);
    delete dotito;
    delete principalSettings;
}

Character::Character(const Character& orig) {
}

int Character::getState() {
    if (isJumping()) {
        return 1; //jumping
    }
    if (isKneeling()) {
        return 2; //kneeling
    }
    if (isFallingDown()) {
        return 3; //fallingDown
    }
    if (isWalking()) {
        return 4; //walking
    } else {
        return 5; //checkEvents
    }
}

bool Character::isJumping() {
    return this->jumping;
}

bool Character::isWalking() {
    return this->walking;
}

bool Character::isKneeling() {
    return this->kneeling;
}

void Character::stopWalking() {
    this->walking = false;
}

bool Character::isFallingDown() {

    this->fallingDown = true;

    if (wasJumping) {
        if (this->object->getBody()->GetLinearVelocity().y <= 0.05) {
            wasJumping = true;
        } else {
            wasJumping = false;
        }
        return fallingDown;
    } else {

        b2Body* body = this->object->getBody();

        for (b2ContactEdge* contList = body->GetContactList(); contList; contList = contList->next) {
            b2Contact* cont = contList->contact;
            b2Fixture* fixtA = cont->GetFixtureA();
            b2Fixture* fixtB = cont->GetFixtureB();
            void* dataVoidA = fixtA->GetUserData();
            int data = *((int*) &dataVoidA);
            if (data == IS_FLOOR && fixtB->IsSensor() && (this->object->getBody()->GetLinearVelocity().y < 0.05)) {
                this->fallingDown = false;
                constFall = initConsFall;
            }
            void* dataVoidB = fixtB->GetUserData();
            data = *((int*) &dataVoidB);
            if (data == IS_FLOOR && fixtA->IsSensor() && (this->object->getBody()->GetLinearVelocity().y < 0.05)) {
                this->fallingDown = false;
                constFall = initConsFall;
            }
        }
        return this->fallingDown;
    }
}

bool Character::isWalkingRight() {
    if (this->walking && this->directionRight) {
        return true;
    }
    return false;
}

bool Character::isWalkingLeft() {
    if (this->walking && !(this->directionRight)) {
        return true;
    }
    return false;
}

void Character::keepFallingDown() {
    this->actualizePosition();
    if (directionRight) {
        changeImage(principalUR1, delay);
    } else {
        changeImage(principalUL1, delay);
    }
}

void Character::jumpRight() {

    b2Body* body = this->object->getBody();
    b2Vec2 velocity((2.0f) * PRINCIPAL_SCALE, (-9.2f) * PRINCIPAL_SCALE);
    body->SetLinearVelocity(velocity);
}

void Character::jumpLeft() {

    b2Body* body = this->object->getBody();
    b2Vec2 velocity((-2.0f) * PRINCIPAL_SCALE, (-9.2f) * PRINCIPAL_SCALE);
    body->SetLinearVelocity(velocity);
}

void Character::moveRight(float velocityX, float velocityY) {

    b2Body* body = this->object->getBody();
    b2Vec2 velocity(velocityX*PRINCIPAL_SCALE, velocityY);
    body->SetLinearVelocity(velocity);
}

void Character::moveLeft(float velocityX, float velocityY) {

    b2Body* body = this->object->getBody();
    b2Vec2 velocity(-velocityX*PRINCIPAL_SCALE, velocityY);
    body->SetLinearVelocity(velocity);
}

void Character::fallRight() {
    this->directionRight = true;
    this->moveRight(5.7f, constFall);
    constFall = constFall + 0.05;
//    cout << constFall << endl;
}

void Character::fallLeft() {
    this->directionRight = false;
    this->moveLeft(5.7f, constFall);
    constFall = constFall + 0.05;
//    cout << constFall << endl;
}

void Character::jumpingRight() {
    //    this->directionRight = true;
    //        this->moveRight(0.5f,-2.0f);
}

void Character::jumpingLeft() {
    //    this->directionRight = false;
    //        this->moveLeft(0.5f,-2.0f);
}

void Character::kneel() {

    this->walking = false;
    this->actualizePosition();
    beginning = false;
    counterKneel++;
    if (counterKneel >= 2) {
        kneeling = true;
        if (directionRight) {
            changeImage(principalDR1, delay);
            counterKneel++;
        } else {
            changeImage(principalDL1, delay);
            counterKneel++;
        }
    }
    SDL_Delay(10);
}

void Character::keepKneeling() {
    this->actualizePosition();
    if (directionRight) {
        switch (this->counterKneel) {
            case (3):
                changeImage(principalDR2, delay);
                counterKneel++;
                break;
            case (4):
                changeImage(principalDR3, delay);
                counterKneel++;
                break;
            case (5):
                changeImage(principalDR2, delay);
                counterKneel++;
                break;
            case (6):
                changeImage(principalDR1, delay);
                //                counterKneel = 1;
                //                kneeling = false;
                break;
        }
    } else {
        switch (this->counterKneel) {
            case (3):
                changeImage(principalDL2, delay);
                counterKneel++;
                break;
            case (4):
                changeImage(principalDL3, delay);
                counterKneel++;
                break;
            case (5):
                counterKneel++;
                changeImage(principalDL2, delay);
                break;
            case (6):
                changeImage(principalDL1, delay);
                //                counterKneel = 1;
                //                kneeling = false;
                break;
        }
    }
    SDL_Delay(10);
}

//void Character::stopKneeling(){
//    this->kneeling = false;
//}

void Character::jump() {

    this->beginning = false;
    counterJump++;
    jumping = true;
    kneeling = false;
    counterKneel = 1;
    if (counterJump >= 2) {
        if (directionRight) {
            //            this->jumpRight();
            this->actualizePosition();
            changeImage(principalDR1, delay);
            counterJump++;

        } else {
            //            this->jumpLeft();
            this->actualizePosition();
            changeImage(principalDL1, delay);
            counterJump++;
        }
    }
}

void Character::keepJumping() {
    if (directionRight) {
        this->actualizePosition();
        switch (this->counterJump) {
            case (3):
                //                this->jumpRight();
                changeImage(principalDR2, delay);
                counterJump++;
                break;
            case (4):
                //                this->jumpRight();
                changeImage(principalDR3, delay);
                counterJump++;
                break;
            case (5):
                //                this->jumpRight();
                changeImage(principalDR2, delay);
                counterJump++;
                break;
            case (6):
                //                this->jumpRight();
                changeImage(principalDR1, delay);
                counterJump++;
                break;
            case (7):
                this->jumpRight();
                changeImage(principalUR1, delay);
                counterJump++;
                break;
            case (8):
                this->jumpRight();
                changeImage(principalUR2, delay);
                counterJump++;
                break;
            case (9):
                changeImage(principalUR3, delay);
                int velY;
                velY = this->object->getBody()->GetLinearVelocity().y;
                if (velY == 0) {
                    counterJump = 1;
                    jumping = false;
                    //                    fallingDown = true;
                    wasJumping = true;
                }
                break;
        }
    } else {
        this->actualizePosition();
        switch (this->counterJump) {
            case (3):
                //                this->jumpLeft();
                changeImage(principalDL2, delay);
                counterJump++;
                break;
            case (4):
                //                this->jumpLeft();
                changeImage(principalDL3, delay);
                counterJump++;
                break;
            case (5):
                //                this->jumpLeft();
                changeImage(principalDL2, delay);
                counterJump++;
                break;
            case (6):
                //                this->jumpLeft();
                changeImage(principalDL1, delay);
                counterJump++;
                break;
            case (7):
                this->jumpLeft();
                changeImage(principalUL1, delay);
                counterJump++;
                break;
            case (8):
                this->jumpLeft();
                changeImage(principalUL2, delay);
                counterJump++;
                break;
            case (9):
                changeImage(principalUL3, delay);
                int velY;
                velY = this->object->getBody()->GetLinearVelocity().y;
                if (velY == 0) {
                    counterJump = 1;
                    jumping = false;
                    //                    fallingDown = true;
                    wasJumping = true;
                }
                break;
        }
    }
}

void Character::wait() {
    this->walking = false;
    kneeling = false;
    counterKneel = 1;
    this->actualizePosition();
    //    float *vec = this->object->getPosition();
    b2Vec2 vec = this->object->getPosition();
    if (this->isFallingDown()) {
        this->keepFallingDown();
    } else {
        if (counterWait > 8) {
            if (this->directionRight) {
                changeImage(principalR, delay);
               
            } else {
                changeImage(principalL, delay);
            }
            velocityWalkRight = 0.0;
            velocityWalkLeft = 0.0;
        } else {
            if (this->directionRight) {
                //                this->walkRight(0);
                changeImage(principalR1, delay);
                counterWait++;
            } else {
                //                this->walkLeft(0);
                changeImage(principalL1, delay);
                counterWait++;
            }
        }
        if (counterWait > 100) {
            counterWait = 9;
        }
    }
}

void Character::keepWalking() {
    if (this->directionRight) {
        if (velocityWalkRight < 4.0){
        velocityWalkRight = velocityWalkRight + 0.5;}
        this->moveRight(velocityWalkRight, 0.0f);
        this->actualizePosition();
        switch (this->counterRight) {
            case (1):
                changeImage(principalR0, delayWalk);

                break;
            case (2):
                changeImage(principalR1, delayWalk);

                break;
            case (3):
                changeImage(principalR2, delayWalk);

                break;
            case (4):
                changeImage(principalR3, delayWalk);

                break;
            case (5):
                changeImage(principalR4, delayWalk);

                break;
            case (6):
                changeImage(principalR3, delayWalk);

                break;
            case (7):
                changeImage(principalR2, delayWalk);

                break;
        }
        if ((counterRightII % 6) == 1) {
            counterRight++;
        }
        counterRightII++;
        if (counterRight == 8) {
            counterRight = 2;
        }
        if (counterRightII > 1000) {
            counterRightII = 1;
        }
    } else {
        if (velocityWalkLeft < 4.0){
        velocityWalkLeft = velocityWalkLeft + 0.5;}
        this->moveLeft(velocityWalkLeft, 0.0f);
        this->actualizePosition();
        switch (this->counterLeft) {
            case (1):
                changeImage(principalL0, delayWalk);

                break;
            case (2):
                changeImage(principalL1, delayWalk);

                break;
            case (3):
                changeImage(principalL2, delayWalk);

                break;
            case (4):
                changeImage(principalL3, delayWalk);

                break;
            case (5):
                changeImage(principalL4, delayWalk);

                break;
            case (6):
                changeImage(principalL3, delayWalk);

                break;
            case (7):
                changeImage(principalL2, delayWalk);

                break;
        }
        if ((counterLeftII % 6) == 1) {
            counterLeft++;
        }
        counterLeftII++;
        if (counterLeft == 8) {
            counterLeft = 2;
        }
        if (counterLeftII > 1000) {

            counterLeftII = 1;
        }
    }
}

void Character::walkRight() {
    kneeling = false;
    counterKneel = 1;
    this->moveRight(velocityWalkRight, 0.0f);
    this->actualizePosition();
    counterWait = 1;
    this->beginning = false;
    changeImage(principalR0, delayWalk);
    this->directionRight = true;
    this->walking = true;
    counterRightII++;
}

void Character::walkLeft() {
    kneeling = false;
    counterKneel = 1;
    this->moveLeft(velocityWalkLeft, 0.0f);
    this->actualizePosition();
    counterWait = 1;
    this->beginning = false;
    this->walking = true;
    changeImage(principalL0, delayWalk);
    this->directionRight = false;
    counterLeftII++;
}

void Character::loadMedia() {

    bool success = true;

    principalR = new SBSImage();
    principalR0 = new SBSImage();
    principalR1 = new SBSImage();
    principalR2 = new SBSImage();
    principalR3 = new SBSImage();
    principalR4 = new SBSImage();
    principalL = new SBSImage();
    principalL0 = new SBSImage();
    principalL1 = new SBSImage();
    principalL2 = new SBSImage();
    principalL3 = new SBSImage();
    principalL4 = new SBSImage();
    principalDL1 = new SBSImage();
    principalDL2 = new SBSImage();
    principalDL3 = new SBSImage();
    principalDR1 = new SBSImage();
    principalDR2 = new SBSImage();
    principalDR3 = new SBSImage();
    principalUL1 = new SBSImage();
    principalUL2 = new SBSImage();
    principalUL3 = new SBSImage();
    principalUR1 = new SBSImage();
    principalUR2 = new SBSImage();
    principalUR3 = new SBSImage();

    principalR->loadMedia("Img/r.png", &gRenderer);
    principalR0->loadMedia("Img/r0.png", &gRenderer);
    principalR1->loadMedia("Img/r1.png", &gRenderer);
    principalR2->loadMedia("Img/r2.png", &gRenderer);
    principalR3->loadMedia("Img/r3.png", &gRenderer);
    principalR4->loadMedia("Img/r4.png", &gRenderer);
    principalL->loadMedia("Img/l.png", &gRenderer);
    principalL0->loadMedia("Img/l0.png", &gRenderer);
    principalL1->loadMedia("Img/l1.png", &gRenderer);
    principalL2->loadMedia("Img/l2.png", &gRenderer);
    principalL3->loadMedia("Img/l3.png", &gRenderer);
    principalL4->loadMedia("Img/l4.png", &gRenderer);
    principalDL1->loadMedia("Img/dL1.png", &gRenderer);
    principalDL2->loadMedia("Img/dL2.png", &gRenderer);
    principalDL3->loadMedia("Img/dL3.png", &gRenderer);
    principalDR1->loadMedia("Img/dR1.png", &gRenderer);
    principalDR2->loadMedia("Img/dR2.png", &gRenderer);
    principalDR3->loadMedia("Img/dR3.png", &gRenderer);
    principalUL1->loadMedia("Img/uL1.png", &gRenderer);
    principalUL2->loadMedia("Img/uL2.png", &gRenderer);
    principalUL3->loadMedia("Img/uL3.png", &gRenderer);
    principalUR1->loadMedia("Img/uR1.png", &gRenderer);
    principalUR2->loadMedia("Img/uR2.png", &gRenderer);
    principalUR3->loadMedia("Img/uR3.png", &gRenderer);

}

Character::~Character() {
    this->close();
    delete this->object;
    delete this->dot;
}

void Character::close() {
    delete principalR;
    delete principalR0;
    delete principalR1;
    delete principalR2;
    delete principalR3;
    delete principalR4;
    delete principalL;
    delete principalL0;
    delete principalL1;
    delete principalL2;
    delete principalL3;
    delete principalL4;
    delete principalDL1;
    delete principalDL2;
    delete principalDL3;
    delete principalDR1;
    delete principalDR2;
    delete principalDR3;
    delete principalUL1;
    delete principalUL2;
    delete principalUL3;
    delete principalUR1;
    delete principalUR2;
    delete principalUR3;

}
